---
title: Constructing CI
description: Learn how Turborepo can help you efficiently complete all the necessary tasks and accelerate your development workflow.
---

import { Callout } from '#components/callout';
import { Tabs, Tab } from 'fumadocs-ui/components/tabs';
import { Step, Steps } from '#components/steps';

Turborepo speeds up builds, lints, tests, and any other tasks that you need to do in your Continuous Integration pipelines. Through parallelization and [Remote Caching](/docs/core-concepts/remote-caching), Turborepo makes your CI dramatically faster.

For examples of how to connect your CI vendor to Remote Cache and run tasks, visit our [CI guides](/docs/guides/ci-vendors).

## Enabling Remote Caching

To enable Remote Caching for your CI, setup the environment variables for Turborepo to access your Remote Cache.

| Environment Variable | Description                                                                                                                                                                                                                                                                                                                  |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TURBO_TOKEN`        | The Bearer token to access the Remote Cache                                                                                                                                                                                                                                                                                  |
| `TURBO_TEAM`         | The account name associated with your repository. When using{' '} <a href="https://vercel.com/docs/monorepos/remote-caching#vercel-remote-cache" rel="noreferrer noopener" target="_blank" >Vercel Remote Cache</a>, this is [your team's slug](https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fsettings&title=Get+team+slug). |

When you run tasks through `turbo`, your CI will be able to hit cache, speeding up your pipelines.

<Callout type="info" title="Remote Cache hosting">
Vercel's built-in CI/CD is automatically connected to your managed Vercel Remote Cache with zero configuration. To retrieve a token for connecting your other CI vendors to Vercel Remote Cache, visit the [Vercel Remote Cache documentation](https://vercel.com/docs/monorepos/remote-caching#use-remote-caching-from-external-ci/cd).

For self-hosted Remote Cache options, visit [Turborepo's Remote Cache documentation](/docs/core-concepts/remote-caching#remote-cache-api).

</Callout>

## Running tasks in CI

By [installing `turbo` globally](/docs/getting-started/installation#global-installation) onto your development and CI machines, you can use one mental model to run your entire repository, from development to ship. The tasks that you've registered in your `turbo.json` will work exactly the same in CI.

- For more information on how to set up tasks, visit the [Configuring Tasks](/docs/crafting-your-repository/configuring-tasks) page.
- For examples of running tasks in CI, visit our [CI guides](/docs/guides/ci-vendors).

### Filtering for entry points

You can filter your tasks using [the `--filter` flag](/docs/reference/run#--filter-string) exactly the same as when you're working with `turbo` locally. Filtering by packages, directories, and Git history are all supported in CI.

<Callout type="info" title="Using Git history in CI">
  Filtering using source control changes is only possible when history is
  available on the machine. If you are using shallow clones, history will not be
  available.
</Callout>

You can also use [the `--affected` flag](#running-only-affected-tasks) to only run tasks in packages that have changes.

## Docker

Docker is an important part of many deployment pipelines. [Turborepo's `prune` subcommand](/docs/reference/prune) helps you ship lightweight images by removing unnecessary dependencies and code from your images.

For more on how to deploy from a Turborepo with Docker, visit [the dedicated Docker guide](/docs/guides/tools/docker).

## Skipping tasks and other unnecessary work

### Running only affected tasks

You can use the `--affected` flag to only run tasks that have changes.

```bash title="Terminal"
turbo run build --affected
```

You'll want to use this flag in situations like:

- You're running many tasks across packages in your monorepo, and only want to run those tasks in packages with code changes.
- You’re _not_ using a Remote Cache, but still want to do as little work as possible in CI.
- You _are_ using a Remote Cache, and you’re in a large repository. By minimizing the amount of tasks that will be restored from cache, there will be less data to send across the network, resulting in faster cache restoration.
- You’re already using [advanced filtering techniques](/docs/reference/run#advanced-filtering-examples) or [`turbo-ignore`](/docs/reference/turbo-ignore) to create the same or similar behavior as `--affected`. You likely have the opportunity to simplify your scripting using this new flag.
  - `--affected` will can handle shallow clones more gracefully than bespoke filtering because it falls back to running all tasks.

#### Using `--affected` in GitHub Actions

CI/CD pipelines are a perfect place to use `--affected`. With `--affected`, Turborepo can automatically detect that you're running in GitHub Actions by inspecting environment variables set by GitHub, like `GITHUB_BASE_REF`.

In the context of a PR, this means that Turborepo can determine which packages have changed between the PR's base branch and the PR's head branch. This allows you to run tasks only for the packages that are affected by the changes in the PR.

While `GITHUB_BASE_REF` works well in `pull_request` and `pull_request_target` events, it is not available during regular push events. In those cases, we use `GITHUB_EVENT_PATH` to determine the base branch to compare your commit to. In force pushes and pushing branch with no additional commits, we compare to the parent of the first commit on the branch.

### Using `turbo-ignore`

As your codebase and CI grow, you may start to look for more ways to get even faster. While hitting cache is useful, you also may be able to skip work entirely. Using `turbo-ignore`, you can skip lengthy container preparation steps like dependency installation that will end up resulting in a cache hit, anyway.

<Steps>
<Step>
### Checkout the repository

Start by cloning your repository. Note that a clone with history to the cloning depth you plan on using is necessary for comparisons.

<Callout type="good-to-know">
  By default, `turbo-ignore` uses the parent commit. To customize for more
  depth, see [the turbo-ignore reference](/docs/reference/turbo-ignore).
</Callout>

</Step>
<Step>
### Run `turbo-ignore` for the package and task

By default, `turbo-ignore` will use the `build` task in the current working directory.

- To check for changes to a different task, use the `--task` flag.
- To check for changes for a specific package and its dependencies, add the package's name as an argument.

<Tabs items={["web#build (Named)", "web#build (Inferred)", "docs#test (--task flag)"]}>
<Tab value="web#build (Named)">

Check for changes for the `build` task for the `web` package and its dependencies by adding the `web` package as an argument:

```bash title="Terminal"
npx turbo-ignore web
```

</Tab>

<Tab value="web#build (Inferred)">

Check for changes for the `build` task for the `web` package and its dependencies using [Automatic Package Scoping](/docs/crafting-your-repository/running-tasks#automatic-package-scoping):

```bash title="Terminal"
cd apps/web
npx turbo-ignore
```

</Tab>

<Tab value="docs#test (--task flag)">

Check for changes for the `test` task for the `docs` package and its dependencies using [Automatic Package Scoping](/docs/crafting-your-repository/running-tasks#automatic-package-scoping) and the `--task` flag:

```bash title="Terminal"
cd apps/docs
npx turbo-ignore --task=test
```

</Tab>
</Tabs>
</Step>

<Step>
### Handle the result

If changes are detected in the package or its [Internal Dependencies](/docs/core-concepts/internal-packages), `turbo` will exit with a `1` status code. If no changes are detected, it will exit with `0`.

Using this status code, you can choose what the rest of your CI pipeline should do. For instance, a `1` exit code likely means that you should move forward with installing dependencies and running tasks.

</Step>
</Steps>
For more advanced use cases, see the [`turbo-ignore` reference](/docs/reference/turbo-ignore).

## Best practices

### Rely on caching

Turborepo's caching abilities allow you to create fast CI pipelines with minimal complexity. Through [Remote Caching](/docs/core-concepts/remote-caching) and using the `--filter` flag to target packages for builds, Turborepo will handle change detection for large monorepos with little overhead.

For example, your CI could run these two commands to quickly handle quality checks and build your target application:

- `turbo run lint check-types test`: Run quality checks for your entire repository. Any packages that haven't changed will hit cache.
- `turbo build --filter=web`: Build the `web` package using the `build` task you've registered in `turbo.json`. If the `web` package or its dependencies haven't changed, the build will also hit cache.

As your codebase scales, you may find more specific opportunities to optimize your CI - but relying on caching is a great place to start.

### Global `turbo` in CI

Using global `turbo` is convenient in CI workflows, allowing you to easily run commands specific to your CI and take advantage of [Automatic Workspace Scoping](/docs/crafting-your-repository/running-tasks#automatic-package-scoping).

However, in some cases, you may be running `turbo` commands or scripts that use `turbo` **before installing packages with your package manager**. One example of this is [using `turbo prune` to create a Docker image](/docs/guides/tools/docker#example). In this situation, global `turbo` will not be able to use the version from `package.json` because the binary for that version hasn't been installed yet.

For this reason, we encourage you to **pin your global installation of `turbo` in CI to the major version in `package.json`** since breaking changes will not be introduced within a major version. You could additionally opt for added stability by pinning an exact version, trading off for maintenance burden to receive bug fixes in patch releases.

### Use `turbo run` in CI

`turbo run` is the most common command you will use when working in your Turborepo so it is aliased to `turbo` for convenience. While this is great for working locally, there are other subcommands for `turbo` like [`turbo prune`](/docs/reference/prune) and [`turbo generate`](/docs/reference/generate).

We're always working to make `turbo` better so we may add more subcommands in the future. For this reason, you can prevent naming collisions by using `turbo run` in your CI.

As an example, if you have a `turbo deploy` command in your CI pipelines, it may conflict with a potential `deploy` subcommand built directly into the `turbo` CLI. To avoid this, use `turbo run deploy` in your CI pipeline instead.

## Troubleshooting

### Hitting cache results in broken builds

If your task is **passing when you miss cache but failing when you hit cache**, you likely haven't configured [the `outputs` key](/docs/reference/configuration#outputs) for your task correctly.

### Deployment using the wrong environment variables

If you haven't defined the `env` or `globalEnv` keys for your task, Turborepo will not be able to use them when creating hashes. This means your task can hit cache despite being in a different environment.

Check your configuration for the `env` and `globalEnv` keys.

## Next steps

You now have everything you need to ship applications with Turborepo. To learn more about specific use cases, [check the Guides](/docs/guides) or [dive deeper into core concepts](/docs/core-concepts).
